/*
 * Powered By XY
 * Since 2020
 */

package com.xy.biz.fd.service.impl;

import com.xy.biz.base.client.DictClient;
import com.xy.biz.base.client.QrCodeClient;
import com.xy.biz.base.domain.resp.ListQrCodeResponse;
import com.xy.biz.fd.client.FolderClient;
import com.xy.biz.fd.domain.DocumentPermissions;
import com.xy.biz.fd.domain.bo.*;
import com.xy.biz.fd.domain.consts.DocumentRubbishConstant;
import com.xy.biz.fd.domain.converter.DocumentConverter;
import com.xy.biz.fd.domain.enums.DocumentTypeEnum;
import com.xy.biz.fd.domain.query.DocumentQuery;
import com.xy.biz.fd.domain.query.DocumentRubbishQuery;
import com.xy.biz.fd.domain.req.PrintQrCodeRequest;
import com.xy.biz.fd.domain.req.document.CopyDocumentRequest;
import com.xy.biz.fd.domain.req.document.GetDocumentChangeRequest;
import com.xy.biz.fd.domain.req.document.GetDocumentRequest;
import com.xy.biz.fd.domain.resp.GetDocumentResponse;
import com.xy.biz.fd.domain.resp.GetDocumentUrlResponse;
import com.xy.biz.fd.domain.resp.GetEditorInfoResponse;
import com.xy.biz.fd.domain.resp.ListDocumentModifyInfoResponse;
import com.xy.biz.fd.manager.*;
import com.xy.biz.fd.manager.impl.MinioDocumentManager;
import com.xy.biz.fd.service.DocumentService;
import com.xy.biz.uc.client.TeamClient;
import com.xy.biz.uc.client.UserClient;
import com.xy.biz.uc.client.UserGroupClient;
import com.xy.biz.uc.domain.dto.UserDTO;
import com.xy.biz.uc.domain.req.GetTeamSRequest;
import com.xy.biz.uc.domain.req.GetUserGroupByRequest;
import com.xy.biz.uc.domain.req.GetUserRequest;
import com.xy.biz.uc.domain.resp.GetUserGroupResponse;
import com.xy.lang.domain.LongIdCodeName;
import com.xy.lang.domain.Result;
import com.xy.lang.exception.ValidationException;
import com.xy.lang.util.CalendarUtils;
import com.xy.lang.util.ResultUtils;
import com.xy.tool.constant.DocumentExtension;
import com.xy.tool.context.UserContextHolder;
import com.xy.tool.gid.service.GidService;
import com.xy.tool.minio.util.LedgerTemplateUtils;
import com.xy.tool.minio.util.MinioUtil;
import com.xy.tool.util.ConvertSizeUtils;
import com.xy.tool.util.FileUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDDocumentInformation;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartHttpServletRequest;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 *
 *
 * @author wwj
 * @date 2021-2-1
 */
@Slf4j
@Service
public class DocumentServiceImpl implements DocumentService {

    @Autowired
    private DocumentManager documentManager;

    @Autowired
    private MinioDocumentManager minioDocumentManager;

    @Autowired
    private DocumentRubbishManager documentRubbishManager;

    @Autowired
    private UserClient userClient;

    @Autowired
    private UserGroupClient userGroupClient;

    @Autowired
    private FolderClient folderClient;

    @Autowired
    private FolderManager folderManager;

    @Autowired
    private RepositoryManager repositoryManager;

    @Autowired
    private DocumentEventBus documentEventBus;

    @Autowired
    private DocumentLogManager documentLogManager;

    @Autowired
    private TeamClient teamClient;

    @Autowired
    private DictClient dictClient;

    @Autowired
    private MinioUtil minioUtil;

    @Autowired
    private GidService gidService;

    @Autowired
    private QrCodeClient qrCodeClient;

    @Autowired
    private LedgerTemplateUtils ledgerTemplateUtils;

    @Autowired
    private RepositoryEventBus repositoryEventBus;

    private static final Logger logger = LoggerFactory.getLogger(DocumentServiceImpl.class);
    private static final String QRCODE_TEMPLATE = "QRCodeTemplate.xlsx";

    @Override
    public Result<? extends GetDocumentResponse> saveDocument(DocumentBO documentBO) {
        GetDocumentResponse getDocumentResponse = documentManager.saveDocument(documentBO);
        if (Objects.nonNull(getDocumentResponse)) {
            documentEventBus.fireDocumentEditHandler(getDocumentResponse.getDocumentId());
            return Result.success(getDocumentResponse);
        }
        return Result.ofErrorRowsAffected();
    }

    @Override
    public Result<?> saveAllDocument(Collection<DocumentBO> collection) {
        List<GetDocumentResponse> affectedRows = documentManager.saveAllDocument(collection);
        if (affectedRows.size() > 0) {
            return Result.success();
        }
        return Result.ofErrorRowsAffected();
    }

    @Override
    public Result<? extends Integer> updateDocument(DocumentBO documentBO) {
        DocumentBO document = documentManager.getDocument(documentBO.getDocumentId());
        if (Objects.isNull(document)) {
            return Result.ofNotFound(String.valueOf(documentBO.getDocumentId()));
        }
        int affectedRows = documentManager.updateDocument(documentBO);
        if (affectedRows > 0) {
            DocumentLogBO documentLogBO = buildDocumentLog(document);
            documentLogBO.setAccessType(DocumentPermissions.RENAME);
            //设置文档操作记录
            documentLogManager.saveDocumentLog(documentLogBO);
            return Result.success(affectedRows);
        }
        return Result.ofErrorRowsAffected();
    }

    @Override
    public Result<? extends Integer> updateAllDocument(Collection<DocumentBO> collection) {
        if (CollectionUtils.isEmpty(collection)) {
            return Result.ofNotFound();
        }
        int affectedRows = documentManager.updateDocumentAll(collection);
        if (affectedRows > 0) {
            List<DocumentLogBO> documentLogBOList = new ArrayList<>();
            collection.forEach(e -> documentLogBOList.add(buildDocumentLog(e)));
            //设置文档操作记录
            documentLogManager.saveAllDocumentLog(documentLogBOList);
            return Result.success(affectedRows);
        }
        return Result.ofErrorRowsAffected();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result<? extends Integer> replaceDocument(MultipartHttpServletRequest request) {
        Long documentId = Long.valueOf(request.getParameter("documentId"));
        Objects.requireNonNull(documentId);
        DocumentBO document = documentManager.getDocument(documentId);
        if (Objects.isNull(document)) {
            return Result.ofNotFound(String.valueOf(documentId));
        }
        DocumentBO documentBO = minioDocumentManager.upLoadDocument(request);
        DocumentBO newDocument = DocumentBO.builder()
                .documentId(document.getDocumentId())
                .path(documentBO.getPath())
                .fileSize(documentBO.getFileSize())
                .build();
        int row = documentManager.updateDocument(newDocument);
        if (row > 0) {
            try {
                minioDocumentManager.removeObject(MinioUtil.storageBucket, document.getPath());
            } catch (Exception e) {
                log.error(e.getMessage());
                return Result.fail(e.getMessage());
            }
            return Result.success();
        }
        return Result.fail();
    }

    @Override
    public Result<? extends Integer> updateDocumentById(DocumentQuery query) {
        int affectedRows = documentManager.updateDocumentByIds(query);
        if (affectedRows > 0) {
            return Result.success(affectedRows);
        }
        return Result.ofErrorRowsAffected();
    }

    @Transactional
    @Override
    public Result<?> updateMoveDocument(DocumentBO documentBO) {
        LocalDateTime now = LocalDateTime.now();
        documentBO.setUpdateTime(now);
        //源folder/repository的Id
        DocumentBO srcDocument = documentManager.getDocument(documentBO.getDocumentId());
        //添加文件路径
        RepositoryBO repository;
        if (documentBO.getRepositoryId() != null) {
            repository = repositoryManager.getRepository(documentBO.getRepositoryId());
        } else {
            repository = repositoryManager.getRepository(srcDocument.getRepositoryId());
        }
        StringBuffer sb = new StringBuffer(repository.getRepositoryName() + "://");
        getFolderName(documentBO.getFolderId(), sb);
        documentBO.setLocation(sb.toString());
        int affectedRows = documentManager.updateDocument(documentBO);
        if (affectedRows > 0) {
            DocumentLogBO documentLogBO = buildDocumentLog(srcDocument);
            documentLogBO.setAccessType(DocumentPermissions.MOVE);
            documentLogManager.saveDocumentLog(documentLogBO);
            documentEventBus.fireDocumentEditHandler(documentBO.getDocumentId());
            documentEventBus.fireDocumentMoveHandler(srcDocument);
            return Result.success();
        }
        return Result.ofErrorRowsAffected();
    }

    @Transactional
    @Override
    public Result<?> deleteDocument(Long documentId) {
        DocumentBO document = documentManager.getDocument(documentId);
        if (Objects.isNull(document)) {
            return Result.ofNotFound("文档不存在", documentId);
        }
        if (Objects.equals(true, document.getDocumentFlowLock())) {
            return Result.fail("文档不存在", "存在当前处于流程中的文件，不可删除！", document.getDocumentName());
        }
        RepositoryBO repositoryBO = documentEventBus.getRubbishRepository();
        if (Objects.isNull(repositoryBO)) {
            return Result.ofErrorStatus("回收站初始化失败");
        }
        //获取文件源folder/repository的Id
        DocumentBO srcDocument = new DocumentBO();
        BeanUtils.copyProperties(document, srcDocument);
        //回收的垃圾文件夹
        DocumentRubbishBO documentRubbishBO = new DocumentRubbishBO();
        LocalDateTime now = LocalDateTime.now();
        documentRubbishBO.setCreateTime(now);
        documentRubbishBO.setUpdateTime(now);
        documentRubbishBO.setTenantId(UserContextHolder.getUserId());
        documentRubbishBO.setRubbishType(DocumentRubbishConstant.GARBAGE_TYPE_FILE);    //文件类型
        documentRubbishBO.setRubbishRepositoryId(repositoryBO.getRepositoryId());    //回收知识库id
        documentRubbishBO.setSourceRepositoryId(document.getRepositoryId());//源知识库id
        documentRubbishBO.setSourceFolderId(document.getFolderId());//源文件id
        documentRubbishBO.setRubbishId(document.getDocumentId());    //垃圾id
        int rows = documentRubbishManager.saveDocumentRubbish(documentRubbishBO);
        if (rows > 0) {
            document.setRepositoryId(repositoryBO.getRepositoryId());
            document.setFolderId(0L);
            document.setUpdateTime(LocalDateTime.now());
            rows = documentManager.updateDocument(document);
            if (rows > 0) {
                DocumentLogBO documentLogBO = buildDocumentLog(document);
                documentLogBO.setAccessType(DocumentPermissions.DELETE);
                documentLogManager.saveDocumentLog(documentLogBO);
                //调用事件总线重新计算文档大小
                documentEventBus.fireDocumentEditHandler(documentId);
                documentEventBus.fireDocumentMoveHandler(srcDocument);
                //删除文档的分享
                documentEventBus.fireDocumentStatusHandler(srcDocument.getDocumentId());
                return Result.success();
            }
        }
        return Result.ofErrorRowsAffected();
    }

    /**
     * 批量删除文档
     *
     * @param request
     */
    @Transactional
    @Override
    public Result<?> deleteDocumentByIds(GetDocumentChangeRequest request) {
        Collection<Long> documentIds = request.getDocumentIds();
        Collection<Long> folderIds = request.getFolderIds();
        Result<?> result = null;
        if (!CollectionUtils.isEmpty(folderIds)) {
            //调用folder的批量删除
            result = folderClient.deleteFolderByIds(folderIds);
            if (result.getCode() != 1 || CollectionUtils.isEmpty(documentIds)) {
                return result;
            }
        }

        List<DocumentBO> documentBOS = documentManager.listDocumentById(documentIds);
        long count = documentBOS.stream().filter(e -> {
            if (Objects.isNull(e.getDocumentFlowLock())) {
                return false;
            }
            return e.getDocumentFlowLock();
        }).count();
        if (count != 0) {
            return Result.ofBadRequest("存在当前处于流程中的文件，不可删除！");
        }
        if (CollectionUtils.isEmpty(documentBOS)) {
            return Result.ofNotFound("文档不存在", documentIds);
        }
        RepositoryBO repositoryBO = documentEventBus.getRubbishRepository();
        if (repositoryBO == null) {
            return Result.ofErrorStatus("回收站初始化失败");
        }
        LocalDateTime now = LocalDateTime.now();
        Long userId = UserContextHolder.getUserId();
        //获取文件源folder/repository的Id
        DocumentBO srcDocument = new DocumentBO();
        BeanUtils.copyProperties(documentBOS.get(0), srcDocument);
        List<DocumentRubbishBO> documentRubbishBOS = new ArrayList<>();
        for (DocumentBO doc : documentBOS) {
            //回收的垃圾文件夹
            DocumentRubbishBO documentRubbishBO = DocumentRubbishBO.builder()
                    .createTime(now)
                    .updateTime(now)
                    .tenantId(userId)
                    .rubbishType(DocumentRubbishConstant.GARBAGE_TYPE_FILE)
                    .rubbishRepositoryId(repositoryBO.getRepositoryId())
                    .sourceRepositoryId(doc.getRepositoryId())
                    .sourceFolderId(doc.getFolderId())
                    .rubbishId(doc.getDocumentId())
                    .build();
            documentRubbishBOS.add(documentRubbishBO);
        }
        int saveRows = documentRubbishManager.saveAllDocumentRubbish(documentRubbishBOS);
        if (saveRows > 0) {
            //改变文档的知识库id和文件夹id
            Set<Long> docIds = documentBOS.stream().map(DocumentBO::getDocumentId).collect(Collectors.toSet());
            //回收站知识库id相同，folder统一改为0，只需documentId即可
            DocumentQuery query = DocumentQuery.builder().repositoryId(repositoryBO.getRepositoryId()).folderId(0L).updateTime(LocalDateTime.now()).documentIds(new ArrayList<>(docIds)).build();
            int updateRows = documentManager.updateDocumentByIds(query);
            if (updateRows > 0) {
                List<DocumentLogBO> documentLogBOS = buildDocumentLogs(documentBOS);
                documentLogBOS.forEach(doc -> doc.setAccessType(DocumentPermissions.DELETE));
                documentLogManager.saveAllDocumentLog(documentLogBOS);
                //当前支持当前页面的文档批量删除，只需调用一次即可
                //调用事件总线重新计算文档大小
                documentEventBus.fireDocumentEditHandler(documentBOS.get(0).getDocumentId());
                documentEventBus.fireDocumentMoveHandler(srcDocument);
                //删除文件夹的分享
                documentIds.forEach(d -> {
                    documentEventBus.fireDocumentStatusHandler(d);
                });
                return Result.success();
            }
        }
        return Result.ofErrorRowsAffected();
    }

    @Override
    public DocumentBO getDocument(Long documentId) {
        return documentManager.getDocument(documentId);
    }

    @Override
    public GetEditorInfoResponse getDocumentByDocument(GetDocumentRequest request) {
        DocumentBO documentBO = documentManager.getDocument(request.getDocumentId());
        if (Objects.isNull(documentBO)) {
            return null;
        }
        Result<UserDTO> user = userClient.getUser(GetUserRequest.builder()
                .userId(UserContextHolder.getUserId())
                .withRole(true).build());
        List<LongIdCodeName> roleList = new ArrayList<>();
        if (user.getData() != null && user.getData().getRoleList() != null) {
            roleList = user.getData().getRoleList();
        }
        Set<String> finalRoleList = roleList.stream().map(LongIdCodeName::getCode).collect(Collectors.toSet());
        //系统管理员满权限
        if (finalRoleList.contains("admin")) {
            documentBO.setPermission(getAllPermissions());
        } else {
            RepositoryBO repository = repositoryManager.getRepository(documentBO.getRepositoryId());
            if (repository.getBelongType() == 0) {
                documentBO.setPermission(getAllPermissions());
            } else if (repository.getBelongType() == 1) {
                //是团队管理员的团队ID
                Set<Long> adminTeamIds = ResultUtils.get(teamClient.isTeamAdminS(GetTeamSRequest.builder().teamIds(new ArrayList<Long>(Collections.singleton(repository.getBelongTo()))).build()));
                if (!CollectionUtils.isEmpty(adminTeamIds) && adminTeamIds.contains(repository.getBelongTo())) {
                    documentBO.setPermission(getAllPermissions());
                } else {
                    documentBO.setPermission(repository.getPermission());
                }
            }
        }
        if (!Objects.isNull(documentBO)) {
            try {
                GetEditorInfoResponse fileMode = minioDocumentManager.generateInfoForEditor(
                        documentBO.getDocumentName(),
                        documentBO.getPath(),
                        UserContextHolder.getUserId(),
                        UserContextHolder.getUserName(),
                        documentBO,
                        request.getMode()
                );
                return fileMode;
            } catch (Exception e) {
                logger.error("获取文档失败！");
                throw new RuntimeException(e);
            }
        }
        return null;
    }

    @Override
    public List<DocumentBO> listDocumentById(Collection<Long> collection) {
        return documentManager.listDocumentById(collection);
    }

    @Override
    public List<DocumentBO> listDocument(DocumentQuery query) {
        List<DocumentBO> documentBOList = documentManager.listDocument(query);
        if (!CollectionUtils.isEmpty(documentBOList)) {
            userClient.buildByUser(documentBOList,
                    documentBOList.stream().map(e -> e.getCreateUserId()).collect(Collectors.toSet()),
                    DocumentBO::getCreateUserId,
                    (documentBO, userDTO) -> documentBO.setCreatedName(userDTO.getUserName()));
        }
        return documentBOList;
    }

    @Override
    public int countDocument(DocumentQuery query) {
        return documentManager.countDocument(query);
    }

    @Override
    public GetDocumentResponse createDocument(DocumentBO documentBO) {

        Integer documentType = documentBO.getDocumentType();

        //新建文档
        try {
            if (DocumentTypeEnum.DOCUMENT.equalsTo(documentType)) {
                return getDocumentCreateResponse(documentBO, DocumentTypeEnum.DOCUMENT.getExt());
            } else if (DocumentTypeEnum.EXECL.equalsTo(documentType)) {
                return getDocumentCreateResponse(documentBO, DocumentTypeEnum.EXECL.getExt());
            } else if (DocumentTypeEnum.PPT.equalsTo(documentType)) {
                return getDocumentCreateResponse(documentBO, DocumentTypeEnum.PPT.getExt());
            }
        } catch (Exception e) {
            logger.error("创建文档失败！");
            throw new RuntimeException(e);
        }
        return null;
    }

    private GetDocumentResponse getDocumentCreateResponse(DocumentBO documentBO, String ext) throws Exception {
        minioDocumentManager.createDemo(
                documentBO,
                ext,
                UserContextHolder.getUserId(),
                UserContextHolder.getUserName());

        //构建文档对象
        buildDocumentBO(documentBO);
        //新建文档
        GetDocumentResponse documentResponse = documentManager.saveDocument(documentBO);
        DocumentLogBO documentLogBO = buildDocumentLog(documentBO);
        documentLogBO.setAccessType(DocumentPermissions.NEW);
        documentLogBO.setDocumentId(documentResponse.getDocumentId());
        documentLogManager.saveDocumentLog(documentLogBO);
        //重新计算文件大小
        documentEventBus.fireDocumentEditHandler(documentResponse.getDocumentId());
        return documentResponse;
    }

    @Override
    public Result<Object> uploadDocument(MultipartHttpServletRequest request) {
        List<DocumentBO> documentBOS;
        try {
            documentBOS = minioDocumentManager.upLoadDocumentList(request);
            String level = request.getParameter("level");
            if (!StringUtils.isEmpty(level)) {
                documentBOS.forEach(e -> e.setLevel(Integer.valueOf(level)));
            }
        } catch (Exception e) {
            e.printStackTrace();
            return Result.ofBadRequest(e.getMessage());
        }
        for (DocumentBO documentBO : documentBOS) {
            try {
                buildDocumentBO(request, documentBO);
            } catch (ValidationException e) {
                e.printStackTrace();
                return Result.ofBadRequest(e.getMessage());
            }
        }
        List<GetDocumentResponse> getDocumentResponses = documentManager.saveAllDocument(documentBOS);
        if (getDocumentResponses.size() > 0) {
            List<DocumentBO> documentBOS1 = DocumentConverter.fromResponse(getDocumentResponses);
            List<DocumentLogBO> documentLogBOS = buildDocumentLogs(documentBOS1);
            documentLogBOS.forEach(doc -> doc.setAccessType(DocumentPermissions.NEW));
            documentLogManager.saveAllDocumentLog(documentLogBOS);
            //重新计算文件大小
            Long docId = CollectionUtils.isEmpty(getDocumentResponses) ? null : getDocumentResponses.get(0).getDocumentId();
            documentEventBus.fireDocumentEditHandler(docId);
            return Result.success(getDocumentResponses);
        }
        return Result.fail("文件上传失败！");
    }

    @Override
    public Result<GetDocumentResponse> copyDocument(CopyDocumentRequest request) {
        DocumentBO srcDocument = documentManager.getDocument(request.getDocumentId());
        DocumentBO documentBO = DocumentBO.builder().accessTime(LocalDateTime.now())
                .createdName(UserContextHolder.getUserName())
                .createTime(LocalDateTime.now())
                .createUserId(UserContextHolder.getUserId())
                .deleteFlag(false)
                .documentType(srcDocument.getDocumentType())
                .documentCode(request.getDocumentCode())
                .fileExtension(srcDocument.getFileExtension())
                .documentName(Objects.nonNull(request.getDocumentName()) ? srcDocument.getDocumentName() + request.getDocumentName() :
                        (srcDocument.getFolderId().equals(request.getDestFolderId()) ?
                                changeDocumentName(srcDocument.getDocumentName()) : srcDocument.getDocumentName()))
                .folderId(Objects.isNull(request.getDestFolderId()) ? 0L : request.getDestFolderId())
                .location(srcDocument.getLocation())
                .permission(0)
                .level(request.getLevel())
                .repositoryId(request.getDestRepositoryId())
                .status(0)
                .tenantId(0L)
                .updateTime(LocalDateTime.now())
                .version("").build();
        minioDocumentManager.copyDocument(srcDocument, documentBO);
        GetDocumentResponse documentResponse = documentManager.saveDocument(documentBO);
        //计算复制后文件大小
        DocumentLogBO documentLogBO = buildDocumentLog(srcDocument);
        documentLogBO.setAccessType(DocumentPermissions.COPY);
        documentLogManager.saveDocumentLog(documentLogBO);
        documentEventBus.fireDocumentEditHandler(documentResponse.getDocumentId());
        return Result.success(documentResponse);
    }

    @Override
    public GetDocumentResponse getDocumentInfoByDocumentId(Long documentId) {
        DocumentBO documentBO = documentManager.getDocument(documentId);
        GetDocumentResponse documentResponse = DocumentConverter.toResponse(documentBO);
        documentResponse.setSize(ConvertSizeUtils.readableFileSize(documentResponse.getFileSize()));
        return documentResponse;
    }

    @Override
    public Result<List<ListDocumentModifyInfoResponse>> listDocumentMotifyInfoByDocumentId(Long documentId) {
        DocumentBO document = documentManager.getDocument(documentId);
        List<DocumentBO> documentBOS = documentManager.listDocument(DocumentQuery.max().threadId(document.getThreadId()).build());
        documentBOS.sort(Comparator.comparing(DocumentBO::getCreateTime).reversed());
        return Result.success(DocumentConverter.toListDocumentModifyInfoResponse(documentBOS));
    }

    @Override
    public List<Object> listDocumentByDocumentName(String keyword) {
        if (StringUtils.isEmpty(keyword)) {
            return Collections.emptyList();
        }
        //定义返回对象
        List<Object> objects = new ArrayList<>();
        //获取当前用户所在分组
        Result<List<GetUserGroupResponse>> listResult = userGroupClient.listMyUserGroup(GetUserGroupByRequest.builder().build());
        //得到分组Id
        Set<Long> groupIds = listResult.getData().stream().map(GetUserGroupResponse::getUserGroupId).collect(Collectors.toSet());
        //通过分组id和个人Id获取知识库Id
        List<RepositoryBO> listRepositoryByUserGroups = repositoryManager.getUserGroupAndRepository(groupIds);
        List<RepositoryBO> repositoryByUserId = repositoryManager.getRepositoryByUserId(UserContextHolder.getUserId());
        Set<Long> listRepositoryId = Stream.concat(listRepositoryByUserGroups.stream(), repositoryByUserId.stream()).map(RepositoryBO::getRepositoryId).collect(Collectors.toSet());
        //通过知识库Id和文件名进行文件查找,可根据文件编码搜索
        DocumentBO documentBO = DocumentBO.builder().repositoryIds(listRepositoryId).documentName(keyword).documentCode(keyword).build();
        List<DocumentBO> documentBOS = documentManager.listDocumentByName(documentBO);
        //通过知识库Id和文件夹名进行文件夹查找
        FolderBO folderBO = FolderBO.builder().repositoryIds(listRepositoryId).folderName(keyword).build();
        List<FolderBO> folderBOS = folderManager.listFolderByName(folderBO);
        if (!CollectionUtils.isEmpty(folderBOS)) {
            folderBOS.sort(Comparator.comparing(FolderBO::getAccessTime).reversed());
            folderBOS.forEach(folder -> {
                folder.setAccessTimeString(CalendarUtils.getDeadlineString(folder.getAccessTime(), LocalDateTime.now()));
            });
            objects.addAll(folderBOS);
        }
        if (!CollectionUtils.isEmpty(documentBOS)) {
            documentBOS.sort(Comparator.comparing(DocumentBO::getAccessTime).reversed());
            documentBOS.forEach(doc -> {
                doc.setAccessTimeString(CalendarUtils.getDeadlineString(doc.getAccessTime(), LocalDateTime.now()));
            });
            objects.addAll(documentBOS);
        }
        return objects;
    }

    /**
     * 批量彻底删除
     *
     * @param request
     */
    @Override
    public Result<?> realDelete(GetDocumentChangeRequest request) {
        Collection<Long> documentIds = request.getDocumentIds();
        Collection<Long> folderIds = request.getFolderIds();
        int rows;
        if (!CollectionUtils.isEmpty(request.getFolderIds())) {
            rows = folderManager.deleteFolders(folderIds);
            if (rows > 0 && CollectionUtils.isEmpty(request.getDocumentIds())) {
                return Result.success();
            }
        }
        if (!CollectionUtils.isEmpty(request.getDocumentIds())) {
            rows = documentManager.deleteDocuments(documentIds);
            if (rows > 0) {
                return Result.success();
            }
        }
        return Result.fail("删除失败");
    }

    @Override
    public Result<?> downloadFile(Long documentId) {
        DocumentBO document = documentManager.getDocument(documentId);
        String url = minioDocumentManager.downloadFile(document.getPath());
        if (Objects.isNull(url)) {
            return Result.ofErrorRowsAffected();
        }
        GetDocumentUrlResponse response = GetDocumentUrlResponse.builder().url(url).documentName(document.getDocumentName() + document.getFileExtension()).build();
        return Result.success(response);
    }

    @Override
    public void getDocumentByPdf(Long documentId, HttpServletResponse response) {
        DocumentBO documentBO = documentManager.getDocument(documentId);
        String path = documentBO.getPath();
        String documentName = documentBO.getDocumentName();
        String fileExtension = documentBO.getFileExtension();
        try {
            InputStream pdfInputStream = minioDocumentManager.getFileInputStream(path);
            byte[] buffer = readInputStream(pdfInputStream);
            response.setHeader("Content-Type", "text/plain");
            response.addHeader("Content-Disposition", "inline;filename=" + new String(documentName.getBytes(), StandardCharsets.UTF_8));
            response.setContentType("application/pdf");
            OutputStream out = response.getOutputStream();
            PDDocument document = PDDocument.load(buffer); //加载pdf
            PDDocumentInformation info = document.getDocumentInformation(); //获得文档属性对象
            info.setTitle(documentName + fileExtension); //修改标题属性  这个标题会被展示
            document.setDocumentInformation(info);
            document.save(out); //修改完直接输出到响应体中
            document.close();
            out.close();
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("获取文件失败");
        }
    }

    public byte[] readInputStream(InputStream inputStream) throws IOException {
        byte[] buffer = new byte[1024];
        int len = 0;
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        while ((len = inputStream.read(buffer)) != -1) {
            bos.write(buffer, 0, len);
        }
        bos.close();
        return bos.toByteArray();
    }

    @Override
    public Result<?> recoverDocument(Long documentId) {

        //通过文档垃圾id查询文档垃圾表
        DocumentRubbishQuery documentRubbishQuery = DocumentRubbishQuery.one().rubbishId(documentId).build();
        List<DocumentRubbishBO> documentRubbishBOS = documentRubbishManager.listDocumentRubbish(documentRubbishQuery);
        if (!CollectionUtils.isEmpty(documentRubbishBOS)) {
            DocumentRubbishBO documentRubbishBO = documentRubbishBOS.get(0);
            //查询文件夹信息
            DocumentBO document = documentManager.getDocument(documentId);
            //源信息
            DocumentBO srcDocument = new DocumentBO();
            BeanUtils.copyProperties(document, srcDocument);
            //修改文件夹 知识库id
            document.setRepositoryId(documentRubbishBO.getSourceRepositoryId());
            document.setFolderId(documentRubbishBO.getSourceFolderId());
            LocalDateTime now = LocalDateTime.now();
            document.setUpdateTime(now);
            int rows = documentManager.updateDocument(document);
            if (rows > 0) {
                //删除文档垃圾内数据
                rows = documentRubbishManager.deleteDocumentRubbish(documentRubbishBO.getDocumentRubbishId());
                if (rows > 0) {
                    //重新计算恢复后文件大小
                    documentEventBus.fireDocumentEditHandler(documentId);
                    documentEventBus.fireDocumentMoveHandler(srcDocument);
                    return Result.success();
                }
            }
        }
        return Result.ofErrorRowsAffected();
    }

    /**
     * 批量恢复文档
     * @param request
     * @return
     */
    @Transactional
    @Override
    public Result<?> recoverDocumentByIds(GetDocumentChangeRequest request) {
        Collection<Long> documentIds = request.getDocumentIds();
        Collection<Long> folderIds = request.getFolderIds();
        Result<?> result = null;
        if (!CollectionUtils.isEmpty(folderIds)) {
            //调用folder的批量恢复
            result = folderClient.recoverFolderByIds(folderIds);
            if (result.getCode() != 1 || CollectionUtils.isEmpty(documentIds)) {
                return result;
            }
        }
        //根据文档id查询出垃圾库中的垃圾文档信息
        List<DocumentRubbishBO> documentRubbishBOS = documentRubbishManager.listDocumentRubbishByRubbishId(documentIds);
        if (!CollectionUtils.isEmpty(documentRubbishBOS)) {
            List<DocumentBO> documentBOS = documentManager.listDocumentById(documentIds);
            //将文档转换为Map类型，便于查找
            Map<Long, DocumentBO> documentBOMap = documentBOS.stream().collect(Collectors.toMap(DocumentBO::getDocumentId, documentBO -> documentBO));
            DocumentBO srcDocument = new DocumentBO();
            LocalDateTime now = LocalDateTime.now();
            ArrayList<DocumentBO> documents = new ArrayList<>();
            //源文档信息
            Map<Long, DocumentBO> srcDocuments = new HashMap<>();
            documentRubbishBOS.forEach(rubbish -> {
                //源信息
                DocumentBO document = documentBOMap.get(rubbish.getRubbishId());
                BeanUtils.copyProperties(document, srcDocument);
                //修改文件夹 知识库id
                document.setRepositoryId(rubbish.getSourceRepositoryId());
                document.setFolderId(rubbish.getSourceFolderId());
                document.setUpdateTime(now);
                documents.add(document);
                srcDocuments.put(document.getDocumentId(), srcDocument);
            });
            int rows = documentManager.updateDocumentAll(documents);
            if (rows > 0) {
                //删除文档垃圾内数据
                Set<Long> ids = documentRubbishBOS.stream().map(DocumentRubbishBO::getDocumentRubbishId).collect(Collectors.toSet());
                rows = documentRubbishManager.deleteDocumentRubbishByIds(ids);
                if (rows > 0) {
                    //根据repositoryId分组
                    Map<Long, List<DocumentBO>> groupByRepository = documents.stream().collect(Collectors.groupingBy(DocumentBO::getRepositoryId));
                    Map<Long, Set<Long>> locationIds = new HashMap<>();
                    //找到每个document的路径Id
                    documents.forEach(doc -> {
                        Set<Long> docIds = new HashSet<>();
                        getLocationIds(doc.getFolderId(), docIds);
                        docIds.add(doc.getDocumentId());
                        locationIds.put(doc.getDocumentId(), docIds);
                    });
                    //根据知识库Id分组，获取到某个知识库中需要恢复的最深层的文件Id
                    Map<Long, Set<Long>> locations = new HashMap<>();
                    for (Long repId : groupByRepository.keySet()) {
                        Set<Long> docIds = groupByRepository.get(repId).stream().map(DocumentBO::getDocumentId).collect(Collectors.toSet());
                        List<Set<Long>> collect = docIds.stream().map(locationIds::get).collect(Collectors.toList());
                        Set<Long> maxSet = getMaxSet(collect);
                        locations.put(repId, maxSet);
                    }
                    //只需计算最深层的文件即可将整个库、文件夹的大小更新
                    for (Long repId : locations.keySet()) {
                        Set<Long> docLocations = locations.get(repId);
                        //获取集合第一个元素
                        Long docId = docLocations.iterator().next();
                        DocumentBO srcDocumentBO = srcDocuments.get(docId);
                        documentEventBus.fireDocumentEditHandler(docId);
                        documentEventBus.fireDocumentMoveHandler(srcDocumentBO);
                    }
                   /* documents.forEach(doc -> {
                        documentEventBus.fireDocumentEditHandler(doc.getDocumentId());
                        documentEventBus.fireDocumentMoveHandler(srcDocuments.get(doc.getDocumentId()));
                    });*/
                    return Result.success();
                }
            }
        }
        return Result.ofErrorRowsAffected();
    }

    /**
     * 打印表格
     *
     * @param request 标识集合
     */
    @Override
    public Result<?> printForm(PrintQrCodeRequest request) throws IOException {
        List<ListQrCodeResponse> qrCodeResponses = ResultUtils.get(qrCodeClient.listQrCodeById(request.getQrIds()));
        //1、加载模板流数据
        InputStream input = ledgerTemplateUtils.getUrl(QRCODE_TEMPLATE);
        //2.根据文件流，加载指定的工作簿
        XSSFWorkbook wb = new XSSFWorkbook(input);
        SXSSFWorkbook workbook = new SXSSFWorkbook(wb);
        //3.读取工作表
        XSSFSheet sheet = wb.getSheetAt(0);
        //4.利用HSSFPatriarch将图片写入EXCEL
        XSSFDrawing drawingPatriarch = sheet.createDrawingPatriarch();
        //5.构造每行和单元格数据
        AtomicInteger datasAi = new AtomicInteger(2);
        CellStyle cellCenter = workbook.createCellStyle();
        CellStyle cellLeft = workbook.createCellStyle();
        cellCenter.setAlignment(HorizontalAlignment.CENTER);
        cellCenter.setVerticalAlignment(VerticalAlignment.CENTER);
        cellCenter.setBorderBottom(BorderStyle.THIN);
        cellCenter.setBorderLeft(BorderStyle.THIN);
        cellCenter.setBorderRight(BorderStyle.THIN);
        cellCenter.setBorderTop(BorderStyle.THIN);
        cellLeft.setAlignment(HorizontalAlignment.LEFT);
        cellLeft.setVerticalAlignment(VerticalAlignment.CENTER);
        cellLeft.setBorderBottom(BorderStyle.THIN);
        cellLeft.setBorderLeft(BorderStyle.THIN);
        cellLeft.setBorderRight(BorderStyle.THIN);
        cellLeft.setBorderTop(BorderStyle.THIN);
        Cell cell = null;
        for (int i = 0; i < qrCodeResponses.size(); i++) {
            ListQrCodeResponse response = qrCodeResponses.get(i);
            int andIncrement = datasAi.getAndIncrement();
            Row dataRow = sheet.createRow(andIncrement);
            dataRow.setHeightInPoints(120);
            //编号
            cell = dataRow.createCell(0);
            cell.setCellValue(1 + i);
            cell.setCellStyle(cellCenter);
            //分类
            cell = dataRow.createCell(1);
            cell.setCellValue(dictClient.getValue(response.getType()));
            cell.setCellStyle(cellCenter);
            //文本
            cell = dataRow.createCell(2);
            cell.setCellValue(response.getText());
            cell.setCellStyle(cellCenter);
            //地址
            cell = dataRow.createCell(3);
            cell.setCellValue(response.getUrl());
            cell.setCellStyle(cellLeft);
            //创建日期
            cell = dataRow.createCell(4);
            DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd");
            cell.setCellValue(response.getCreateTime().format(df));
            cell.setCellStyle(cellCenter);
            //二维码
            XSSFClientAnchor anchor = new XSSFClientAnchor(0, 0, 0, 0, (short) 5, andIncrement, (short) 5, andIncrement);
            anchor.setAnchorType(ClientAnchor.AnchorType.MOVE_AND_RESIZE);
            XSSFPicture picture = drawingPatriarch.createPicture(anchor, wb.addPicture(response.getQrCode(), HSSFWorkbook.PICTURE_TYPE_JPEG));
            picture.resize(1.05, 1.10);
        }
        //生成Excel
        FileInputStream inputStream = null;
        long mpath = gidService.generate();
        String serverFolder = FileUtils.getPath();
        String path = null;
        OutputStream out = null;
        try {
            path = mpath + ".xlsx";
            out = new FileOutputStream(serverFolder + path);
            workbook.write(out);
            inputStream = new FileInputStream(serverFolder + path);
            //删除原临时文件
            FileUtils.delPath(serverFolder + path);
        } catch (Exception e) {
            log.error(e.getMessage());
        } finally {
            try {
                workbook.close();
                out.close();
            } catch (Exception e) {
                log.error("At IO close：" + e.getMessage());
            }
        }
        Long size = null;
        try {
            size = minioUtil.uploadFile(inputStream, path, MinioUtil.storageBucket);
        } catch (Exception e) {
            log.error(e.getMessage());
        } finally {
            if (size == null) {
                return Result.fail();
            }
        }
        LocalDateTime now = LocalDateTime.now();
        DocumentBO build = DocumentBO.builder()
                .documentName(request.getName())
                .documentType(2)
                .folderId(0L)
                .repositoryId(0L)
                .status(0)
                .fileExtension(DocumentExtension.EXCELX)
                .fileSize(size)
                .path(path)
                .location("")
                .level(0)
                .version("A0")
                .threadId(0L)
                .tenantId(0L)
                .permission(DocumentPermissions.getAllPermissions())
                .accessTime(now)
                .createUserId(UserContextHolder.getUserId())
                .createTime(now)
                .updateTime(now)
                .documentFlowLock(false)
                .deleteFlag(false)
                .build();
        Long documentId = ResultUtils.get(saveDocument(build)).getDocumentId();
        //将文件移到固定文件夹下
        repositoryEventBus.firerRepositoryHandler(documentId);
        return Result.success(documentId);
    }

    private void buildDocumentBO(DocumentBO documentBO) {
        String minObjectName = documentBO.getPath();

        documentBO.setCreateUserId(UserContextHolder.getUserId());
        documentBO.setFileExtension(minObjectName.substring(minObjectName.indexOf(".")));
        documentBO.setThreadId(Long.valueOf(minObjectName.substring(0, minObjectName.indexOf("."))));
        LocalDateTime now = LocalDateTime.now();
        documentBO.setAccessTime(now);
        documentBO.setCreateTime(now);
        documentBO.setUpdateTime(now);
        //添加文件路径
        RepositoryBO repository = repositoryManager.getRepository(documentBO.getRepositoryId());
        StringBuffer sb = new StringBuffer(repository.getRepositoryName() + "://");
        getFolderName(documentBO.getFolderId(), sb);
        //sb.deleteCharAt(sb.length() - 1);
        documentBO.setLocation(sb.toString());
        documentBO.setTenantId(UserContextHolder.getTenantId());
        documentBO.setStatus(0);
        documentBO.setPermission(0);
        documentBO.setVersion("");
        documentBO.setDeleteFlag(Boolean.FALSE);
    }

    private void buildDocumentBO(HttpServletRequest request, DocumentBO documentBO) throws ValidationException {
        String repositoryId = request.getParameter("repositoryId");
        if (StringUtils.isEmpty(repositoryId)) {
            throw new ValidationException("参数repositoryId为空");
        }
        try {
            Long repositoryIdValue = Long.valueOf(repositoryId);
            documentBO.setRepositoryId(repositoryIdValue);
        } catch (Exception e) {
            throw new ValidationException("参数repositoryId格式不正确");
        }
        String folderId = request.getParameter("folderId");
        if (StringUtils.isEmpty(folderId)) {
            throw new ValidationException("参数folderId为空");
        }
        try {
            Long folderIdValue = Long.valueOf(folderId);
            documentBO.setFolderId(folderIdValue);
        } catch (Exception e) {
            throw new ValidationException("参数folderIdValue格式不正确");
        }
        String documentCode = request.getParameter("documentCode");
        documentBO.setDocumentCode(documentCode);
        documentBO.setPermission(0);
        String minObjectName = documentBO.getMinObjectName();
        documentBO.setThreadId(Long.valueOf(minObjectName.substring(0, minObjectName.indexOf("."))));
        documentBO.setVersion("");
        documentBO.setStatus(0);
        documentBO.setFileExtension(minObjectName.substring(minObjectName.indexOf(".")));
        LocalDateTime now = LocalDateTime.now();
        //添加文件路径
        RepositoryBO repository = repositoryManager.getRepository(documentBO.getRepositoryId());
        StringBuffer sb = new StringBuffer(repository.getRepositoryName() + "://");
        getFolderName(documentBO.getFolderId(), sb);
        documentBO.setLocation(sb.toString());
        documentBO.setAccessTime(now);
        documentBO.setCreateTime(now);
        documentBO.setUpdateTime(now);
        documentBO.setCreateUserId(UserContextHolder.getUserId());
        documentBO.setDeleteFlag(Boolean.FALSE);
        documentBO.setTenantId(UserContextHolder.getTenantId());
    }


    /**
     * @author Greekn Zhang
     * @date 2021/4/29 14:05
     * @param folderId
     * @param sb
     * @return java.lang.StringBuffer
     * @description 递归获取文件夹名字
     */
    private StringBuffer getFolderName(Long folderId, StringBuffer sb) {
        if (!Objects.isNull(folderId) && folderId != 0) {
            FolderBO folder = folderManager.getFolder(folderId);
            getFolderName(folder.getParentFolderId(), sb);
            return sb.append(folder.getFolderName()).append("/");
        } else {
            return null;
        }
    }

    /**
     * @author Greekn Zhang
     * @date 2021/4/30 11:55
     * @param folderId
     * @param ids
     * @description 递归获取文件夹路径的id
     */
    private void getLocationIds(Long folderId, Set<Long> ids) {
        FolderBO folder = folderManager.getFolder(folderId);
        if (folder != null) {
            ids.add(folder.getFolderId());
            getLocationIds(folder.getParentFolderId(), ids);
        } else {
            return;
        }
    }

    /**
     * @author Greekn Zhang
     * @date 2021/4/30 15:08
     * @param list
     * @return java.util.Set<java.lang.Long>
     * @description 获取最大set集合
     */
    private Set<Long> getMaxSet(List<Set<Long>> list) {
        Set<Long> set = new HashSet<>();
        for (int i = 0; i < list.size(); i++) {
            if (list.get(i).size() >= set.size()) {
                set = new HashSet<>(list.get(i));
            }
        }
        return set;
    }

    /**
     * @author Greekn Zhang
     * @date 2021/5/11 16:35
     * @return java.lang.String
     * @description 复制文件时若相同文件下有相同名文件则会进行改名
     */
    private String changeDocumentName(String documentName) {
        StringBuffer stringBuffer = new StringBuffer(documentName);
        if (documentName.contains(" - 副本")) {
            String substring = documentName.substring(documentName.lastIndexOf("本") + 1);
            if (!("").equals(substring) && org.apache.commons.lang3.StringUtils.isNumeric(substring)) {
                Long num = Long.parseLong(substring);
                return stringBuffer.deleteCharAt(stringBuffer.length() - 1).append(++num).toString();
            }
            return stringBuffer.append(2).toString();
        }
        return stringBuffer.append(" - 副本").toString();
    }

    private DocumentLogBO buildDocumentLog(DocumentBO documentBO) {
        DocumentLogBO documentLogBO = new DocumentLogBO();
        documentLogBO.setDocumentId(documentBO.getDocumentId());
        documentLogBO.setDocumentName(documentBO.getDocumentName());
        documentLogBO.setCreateTime(LocalDateTime.now());
        documentLogBO.setCreateUserId(UserContextHolder.getUserId());
        documentLogBO.setVersion(documentBO.getVersion());
        documentLogBO.setTenantId(UserContextHolder.getTenantId());
        documentLogBO.setAccessType(Objects.isNull(documentBO.getPermission()) ? 0 : documentBO.getPermission());
        documentLogBO.setDeleteFlag(false);
        return documentLogBO;
    }

    private List<DocumentLogBO> buildDocumentLogs(Collection<DocumentBO> documentBOs) {
        List<DocumentLogBO> documentLogBOS = new ArrayList<>();
        for (DocumentBO documentBO : documentBOs) {
            DocumentLogBO documentLogBO = new DocumentLogBO();
            documentLogBO.setDocumentId(documentBO.getDocumentId());
            documentLogBO.setDocumentName(documentBO.getDocumentName());
            documentLogBO.setCreateTime(LocalDateTime.now());
            documentLogBO.setCreateUserId(UserContextHolder.getUserId());
            documentLogBO.setVersion(documentBO.getVersion());
            documentLogBO.setTenantId(UserContextHolder.getTenantId());
            documentLogBO.setDeleteFlag(false);
            documentLogBOS.add(documentLogBO);
        }
        return documentLogBOS;
    }

    private Integer getAllPermissions() {
        Integer permissions = DocumentPermissions.VISIBLE +
                DocumentPermissions.PREVIEW +
                DocumentPermissions.PRINT +
                DocumentPermissions.DOWNLOAD +
                DocumentPermissions.RENAME +
                DocumentPermissions.FAVORITE +
                DocumentPermissions.MOVE +
                DocumentPermissions.COPY +
                DocumentPermissions.EDIT +
                DocumentPermissions.NEW +
                DocumentPermissions.SHARE +
                DocumentPermissions.ASSOCIATE +
                DocumentPermissions.DELETE;
        return permissions;
    }
}
